﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

namespace DataService.Core.DataHelper
{
    public static class DataTableHelper
    {
        private delegate void LoadDataTable<T>(DataTable dr, T obj);
        private delegate void LoadDataRow<T>(DataRow row, T obj);
        private static IDictionary<string, object> cache = new ConcurrentDictionary<string, object>();
        private static IDictionary<string, DataTable> cacheDataTable = new ConcurrentDictionary<string, DataTable>();
        private delegate T Load<T>(DataRow dataRecord);
        //用于构造Emit的DataRow中获取字段的方法信息
         private static readonly MethodInfo getValueMethod = typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(int)});
        //用于构造Emit的DataRow中判断是否为空行的方法信息
        private static readonly MethodInfo isDBNullMethod = typeof(DataRow).GetMethod("IsNull", new Type[] { typeof(int) });
        //使用字典存储实体的类型以及与之对应的Emit生成的转换方法
        private static IDictionary<Type, Delegate> rowMapMethods = new ConcurrentDictionary<Type, Delegate>();
        //使用字典存储实体的类型以及与datatable之间的列
        private static IDictionary<Type, string> rowMapColumn = new ConcurrentDictionary<Type, string>();
        public static DataTable ListToTable<T>(this IList<T> lst)
        {
            DataTable dt = new DataTable();
            if (!cacheDataTable.ContainsKey(typeof(T).FullName))
            {
                var properties = typeof(T).GetProperties();
                foreach (var p in properties)
                {
                    var cur = Nullable.GetUnderlyingType(p.PropertyType);
                    dt.Columns.Add(p.Name, cur == null ? p.PropertyType : cur);
                }
            }
            else
            {
                dt = cacheDataTable[typeof(T).FullName].Clone();
            }
            //1.如果调用table转换
            //LoadDataTable<T> load = (LoadDataTable<T>)PersonToDataTable<T>().CreateDelegate(typeof(LoadDataTable<T>));
            LoadDataTable<T> load = Find<T>();
            foreach (var item in lst)
            {
                load(dt, item);
            }
            return dt;
        }
        public static List<T> DataTableToList<T>(DataTable dt)
        {
            List<T> list = new List<T>();
            if (dt == null)
            {
                return list;
            }
            //声明 委托Load<T>的一个实例rowMap
            Load<T> rowMap = null;
            //1.先验证缓存的列名与现有列名是否一致
            if (rowMapColumn.ContainsKey(typeof(T)))
            {
                string columnForamte = rowMapColumn[typeof(T)];
                StringBuilder stringBuilder = new StringBuilder();
                for (int k = 0; k < dt.Columns.Count; k++)
                {
                    stringBuilder.Append(dt.Columns[k].ColumnName).Append(",");
                }
                stringBuilder.Length--;
                //与缓存的列不一致,需要清除动态类型的缓存
                if (stringBuilder.ToString() != columnForamte)
                {
                    rowMapColumn.Remove(typeof(T));
                }
            }
            //从rowMapMethods查找当前T类对应的转换方法，没有则使用Emit构造一个。
            if (!rowMapMethods.ContainsKey(typeof(T)))
            {
                DynamicMethod method = new DynamicMethod("DynamicCreateEntity_" + typeof(T).Name, typeof(T), new Type[] { typeof(DataRow) }, typeof(T), true);
                ILGenerator generator = method.GetILGenerator();
                LocalBuilder result = generator.DeclareLocal(typeof(T));
                generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
                generator.Emit(OpCodes.Stloc, result);
                StringBuilder stringBuilder = new StringBuilder();
                for (int k = 0; k < dt.Columns.Count; k++)
                {
                    stringBuilder.Append(dt.Columns[k].ColumnName).Append(",");
                    PropertyInfo propertyInfo = typeof(T).GetProperty(dt.Columns[k].ColumnName);
                    Label endIfLabel = generator.DefineLabel();
                    
                  
                    if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
                    {
                        generator.Emit(OpCodes.Ldarg_0);
                        generator.Emit(OpCodes.Ldc_I4, k);
                        generator.Emit(OpCodes.Callvirt, isDBNullMethod);
                        generator.Emit(OpCodes.Brtrue, endIfLabel);
                        generator.Emit(OpCodes.Ldloc, result);
                        generator.Emit(OpCodes.Ldarg_0);
                        generator.Emit(OpCodes.Ldc_I4, k);
                        generator.Emit(OpCodes.Callvirt, getValueMethod);

                        generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
                        generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
                        generator.MarkLabel(endIfLabel);
                    }
                }
                generator.Emit(OpCodes.Ldloc, result);
                generator.Emit(OpCodes.Ret);
                //构造完成以后传给rowMap
                rowMap = (Load<T>)method.CreateDelegate(typeof(Load<T>));
                rowMapMethods[typeof(T)] = rowMap;
                stringBuilder.Length--;
                rowMapColumn[typeof(T)] = stringBuilder.ToString();
            }
            else
            {
                rowMap = (Load<T>)rowMapMethods[typeof(T)];//调用缓存
            }
            //遍历Datatable的rows集合，调用rowMap把DataRow转换为对象（T）
            foreach (DataRow info in dt.Rows)
            {
                list.Add(rowMap(info));
            }
            return list;

        }



        /// <summary>
        /// 忽略特性的查找
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        private static LoadDataTable<T> Find<T>()
        {
            LoadDataTable<T> load = null;
            object v = null;
            string name = typeof(T).FullName;
            if (cache.TryGetValue(name, out v))
            {
                load = v as LoadDataTable<T>;
            }
            else
            {
                load = (LoadDataTable<T>)ListToDataTableByEmit<T>().CreateDelegate(typeof(LoadDataTable<T>));
                cache[name] = load;
            }
            return load;
        }
        /// <summary>
        /// 直接转换整个DataTable
        /// </summary>
        /// <typeparam name="T">model</typeparam>
        /// <param name="map">列名称映射:Key列名称，Value属性名称</param>
        /// <param name="mapType">列类型映射：key列类型，value属性类型</param>
        /// <returns></returns>
        private static DynamicMethod ListToDataTableByEmit<T>(Dictionary<string, string> map = null, Dictionary<string, Type> mapType = null)
        {
            DynamicMethod method = new DynamicMethod(typeof(T).Name + "ToDataTable", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, null,
                new Type[] { typeof(DataTable), typeof(T) }, typeof(EntityContext).Module, true);
            ILGenerator generator = method.GetILGenerator();
            //创建行 实现DataRow row=dt.NewRow();
            LocalBuilder reslut = generator.DeclareLocal(typeof(DataRow));
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Call, typeof(DataTable).GetMethod("NewRow"));
            generator.Emit(OpCodes.Stloc, reslut);//结果存储
            var properties = typeof(T).GetProperties();
            //
            Dictionary<string, Type> dic = new Dictionary<string, Type>();
            Dictionary<string, LocalBuilder> dicLocalBuilder = new Dictionary<string, LocalBuilder>();
            List<PropertyInfo> lstNull = new List<PropertyInfo>();

            //获取空类型属性
            foreach (var item in properties)
            {
                if (Nullable.GetUnderlyingType(item.PropertyType) != null)
                {
                    lstNull.Add(item);
                }
            }
            int cout = lstNull.Count;
            List<LocalBuilder> lstLocal = new List<LocalBuilder>(lstNull.Count);
            foreach (var item in lstNull)
            {
                //获取所有空类型属性的类型
                dic[item.Name] = item.PropertyType;
            }

            for (int i = 0; i < cout; i++)
            {
                //定义足够的bool
                lstLocal.Add(generator.DeclareLocal(typeof(bool)));
            }
            foreach (var kv in dic)
            {
                //定义包含的空类型
                if (!dicLocalBuilder.ContainsKey(kv.Value.FullName))
                {
                    dicLocalBuilder[kv.Value.FullName] = generator.DeclareLocal(kv.Value);
                }
            }
            //没有列名称映射
            int index = -1;//必须-1合适
            if (map == null)
            {
                //遍历属性
                foreach (var p in properties)
                {
                    if (dic.ContainsKey(p.Name))
                    {
                        var endIfLabel = generator.DefineLabel();
                        //判断
                        //
                        generator.Emit(OpCodes.Ldarg_1);
                        generator.Emit(OpCodes.Call, p.GetGetMethod());//

                        generator.Emit(OpCodes.Stloc_S, dicLocalBuilder[p.PropertyType.FullName]);
                        generator.Emit(OpCodes.Ldloca_S, dicLocalBuilder[p.PropertyType.FullName]);
                        generator.Emit(OpCodes.Call, dic[p.Name].GetMethod("get_HasValue"));

                        generator.Emit(OpCodes.Stloc, lstLocal[++index]);
                        generator.Emit(OpCodes.Ldloc, lstLocal[index]);
                        generator.Emit(OpCodes.Brfalse_S, endIfLabel);
                        //赋值
                        generator.Emit(OpCodes.Ldloc, reslut);//取出变量
                        generator.Emit(OpCodes.Ldstr, p.Name);//row["Name"]
                        generator.Emit(OpCodes.Ldarg_1);
                        generator.Emit(OpCodes.Call, p.GetGetMethod());//
                                                                       //
                        generator.Emit(OpCodes.Stloc_S, dicLocalBuilder[p.PropertyType.FullName]);
                        generator.Emit(OpCodes.Ldloca_S, dicLocalBuilder[p.PropertyType.FullName]);
                        generator.Emit(OpCodes.Call, dic[p.Name].GetMethod("get_Value"));
                        //
                        generator.Emit(OpCodes.Box, Nullable.GetUnderlyingType(p.PropertyType));//有点麻烦
                                                                                                //
                        generator.Emit(OpCodes.Call, typeof(DataRow).GetMethod("set_Item", new Type[] { typeof(string), typeof(object) }));
                        generator.MarkLabel(endIfLabel);
                    }
                    else
                    {
                        generator.Emit(OpCodes.Ldloc, reslut);
                        generator.Emit(OpCodes.Ldstr, p.Name);
                        generator.Emit(OpCodes.Ldarg_1);
                        generator.Emit(OpCodes.Call, p.GetGetMethod());//获取属性
                        if (mapType == null || !mapType.ContainsKey(p.Name))
                        {
                            if (p.PropertyType.IsValueType)
                                generator.Emit(OpCodes.Box, p.PropertyType);//有点麻烦
                            else
                                generator.Emit(OpCodes.Castclass, p.PropertyType);
                        }
                        else
                        {
                            generator.Emit(OpCodes.Ldtoken, mapType[p.Name]);
                            generator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
                            generator.Emit(OpCodes.Call, typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) }));
                        }
                        generator.Emit(OpCodes.Call, typeof(DataRow).GetMethod("set_Item", new Type[] { typeof(string), typeof(object) }));
                    }
                }
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Call, typeof(DataTable).GetMethod("get_Rows"));
                generator.Emit(OpCodes.Ldloc, reslut);
                generator.Emit(OpCodes.Call, typeof(DataRowCollection).GetMethod("Add", new Type[] { typeof(DataRow) }));
                generator.Emit(OpCodes.Ret);

            }
            else
            {

                List<PropertyInfo> lst = new List<PropertyInfo>(properties);
                foreach (var kv in map)
                {
                    var p = lst.Find(x => x.Name == kv.Value);//找到属性
                    if (dic.ContainsKey(p.Name))
                    {
                        var endIfLabel = generator.DefineLabel();
                        //判断
                        //
                        generator.Emit(OpCodes.Ldarg_1);
                        generator.Emit(OpCodes.Call, p.GetGetMethod());//

                        generator.Emit(OpCodes.Stloc_S, dicLocalBuilder[p.PropertyType.FullName]);
                        generator.Emit(OpCodes.Ldloca_S, dicLocalBuilder[p.PropertyType.FullName]);
                        generator.Emit(OpCodes.Call, dic[p.Name].GetMethod("get_HasValue"));

                        generator.Emit(OpCodes.Stloc, lstLocal[++index]);
                        generator.Emit(OpCodes.Ldloc, lstLocal[index]);
                        generator.Emit(OpCodes.Brfalse_S, endIfLabel);
                        //赋值
                        generator.Emit(OpCodes.Ldloc, reslut);
                        generator.Emit(OpCodes.Ldstr, kv.Value);//属性名称
                        generator.Emit(OpCodes.Ldarg_1);
                        generator.Emit(OpCodes.Call, p.GetGetMethod());//
                                                                       //
                        generator.Emit(OpCodes.Stloc_S, dicLocalBuilder[p.PropertyType.FullName]);
                        generator.Emit(OpCodes.Ldloca_S, dicLocalBuilder[p.PropertyType.FullName]);
                        generator.Emit(OpCodes.Call, dic[p.Name].GetMethod("get_Value"));
                        //
                        generator.Emit(OpCodes.Box, Nullable.GetUnderlyingType(p.PropertyType));//有点麻烦
                                                                                                //
                        generator.Emit(OpCodes.Call, typeof(DataRow).GetMethod("set_Item", new Type[] { typeof(string), typeof(object) }));
                        generator.MarkLabel(endIfLabel);
                    }
                    else
                    {
                        generator.Emit(OpCodes.Ldloc, reslut);
                        generator.Emit(OpCodes.Ldstr, kv.Value);//属性名称
                        generator.Emit(OpCodes.Ldarg_1);
                        generator.Emit(OpCodes.Call, p.GetGetMethod());//获取属性值
                        if (mapType == null || !mapType.ContainsKey(kv.Key))
                        {
                            if (p.PropertyType.IsValueType)
                                generator.Emit(OpCodes.Box, p.PropertyType);//这里有点麻烦
                            else
                                generator.Emit(OpCodes.Castclass, p.PropertyType);
                        }
                        else
                        {
                            generator.Emit(OpCodes.Ldtoken, mapType[kv.Key]);
                            generator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
                            generator.Emit(OpCodes.Call, typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) }));
                        }

                        generator.Emit(OpCodes.Call, typeof(DataRow).GetMethod("set_Item", new Type[] { typeof(string), typeof(object) }));
                    }

                }
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Call, typeof(DataTable).GetMethod("get_Rows"));
                generator.Emit(OpCodes.Ldloc, reslut);
                generator.Emit(OpCodes.Call, typeof(DataRowCollection).GetMethod("Add", new Type[] { typeof(DataRow) }));
                generator.Emit(OpCodes.Ret);
            }
            return method;

        }
    }

    public class EntityContext
    {
    }
}
